package com.hundsun.data.services.impl;

import cn.afterturn.easypoi.excel.entity.ExportParams;
import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson2.JSON;
import com.hundsun.data.core.DynamicDataSourceContextHolder;
import com.hundsun.data.dto.DataDiffDto;
import com.hundsun.data.enums.DataSourceKey;
import com.hundsun.data.mapper.DataDiffMapper;
import com.hundsun.data.po.TableInfoPo;
import com.hundsun.data.services.IDataDiffServices;
import com.hundsun.data.uitls.ExcelExportUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author pw
 * @date 2024年06月12日
 * @describe
 **/
@Service
@Slf4j
public class DataDiffServicesImpl implements IDataDiffServices {

    @Autowired
    DataDiffMapper dataDiffMapper;

    @Autowired
    ExcelExportUtils exportUtils;

    @Override
    public List<DataDiffDto> getDiff(String type) {
        DynamicDataSourceContextHolder.setDataSourceType(DataSourceKey.DM.name());
        List<TableInfoPo> infoPos = dataDiffMapper.selectDm(null);
        log.info("dm size:{}", infoPos.size());

        DynamicDataSourceContextHolder.setDataSourceType(DataSourceKey.ORACLE.name());
        List<TableInfoPo> selectOracle = dataDiffMapper.selectOracle(null);
        log.info("oracle size:{}", selectOracle.size());
        // dm 与oracle表结构的差异
        List<TableInfoPo> diffByTableInfoPo = getDiffByTableInfoPo(infoPos, selectOracle);
        ExportParams entity = new ExportParams();
        entity.setSheetName("Oracle与dm表差异");
        //导出 dm 与oracle表结构的差异
        exportUtils.exportByEntity(entity, TableInfoPo.class, diffByTableInfoPo, "Oracle与DM表差异.xlsx");

        DynamicDataSourceContextHolder.setDataSourceType(DataSourceKey.TD.name());
        List<TableInfoPo> tableInfoPos = dataDiffMapper.selectTd(null);
        log.info("td size:{}", tableInfoPos.size());

        ExportParams entityParam = new ExportParams();
        entityParam.setSheetName("Oracle与Td表差异");
        // td 与oracle表结构的差异
        List<TableInfoPo> diffOracleTd = getDiffByTableInfoPo(tableInfoPos, selectOracle);
        //导出td 与oracle表结构的差异
        exportUtils.exportByEntity(entityParam, TableInfoPo.class, diffOracleTd, "Oracle与Td表差异.xlsx");
        return List.of();
    }

    private List<TableInfoPo> getDiffByTableInfoPo(List<TableInfoPo> source, List<TableInfoPo> target) {
        Map<String, TableInfoPo> sourceMap = source.stream()
                .sorted(Comparator.comparing(TableInfoPo::getTableName))
                .collect(Collectors.toMap((it -> it.getTableName().toUpperCase() + "_" + it.getColumnName().toUpperCase()), each -> each, (value1, value2) -> value1, LinkedHashMap::new));
        Map<String, TableInfoPo> targetMap = target.stream()
                .sorted(Comparator.comparing(TableInfoPo::getTableName))
                .collect(Collectors.toMap((it -> it.getTableName().toUpperCase() + "_" + it.getColumnName().toUpperCase()), each -> each, (value1, value2) -> value1, LinkedHashMap::new));
        int sourceSize = sourceMap.size();
        int targetSize = targetMap.size();
        List<TableInfoPo> result = new ArrayList<>();
        if (sourceSize > targetSize) {
            getDiff(sourceMap, targetMap, result);
            if (!targetMap.isEmpty()) {
                targetMap.forEach((k, v) -> result.add(v));
            }
        } else {
            getDiff(targetMap, sourceMap, result);
            if (!sourceMap.isEmpty()) {
                sourceMap.forEach((k, v) -> result.add(v));
            }
        }
        return result;
    }

    private void getDiff(Map<String, TableInfoPo> sourceMap, Map<String, TableInfoPo> targetMap, List<TableInfoPo> result) {
        sourceMap.forEach((k, v) -> {
            List<TableInfoPo> infoPo = diffPo(v, targetMap.get(k));
            if (infoPo != null && !infoPo.isEmpty()) {
                result.addAll(infoPo);
            }
            targetMap.remove(k);
        });
    }

    private List<TableInfoPo> diffPo(TableInfoPo info, TableInfoPo tableInfoPo) {
        List<TableInfoPo> diffs = new ArrayList<>();
        if (tableInfoPo == null) {
            diffs.add(info);
            return diffs;
        }
        if (info.equals(tableInfoPo)) {
            return null;
        }
        diffs.add(info);
        diffs.add(tableInfoPo);
        return diffs;
    }
}
